#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>

static const int gmax = 5;

template <class T>
class BlockQueue//一个简单生产消费模型
{
public:
    BlockQueue(int max = gmax):max_(max)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&p_cond_, nullptr);
        pthread_cond_init(&c_cond_, nullptr);
    }
    void push(const T& d)//对于输入参数用 const &
    {
        pthread_mutex_lock(&mutex_);
        //使用while，可以避免虚假唤醒，因为当线程被唤醒的时候，可能并没有真正的满足条件变量，这时候可能会导致错误
        while(is_max())
        {
            //细节：当调用pthread_cond_wait时候，会先将互斥锁释放，然后线程进入阻塞队列中
            //当该函数调用结束时，会将互斥锁再拿回来，这也就是为什么我们需要把mutex放入该函数的原因
            //而且该mutex还得必须是我们使用的
            pthread_cond_wait(&p_cond_, &mutex_);//缓冲区已经满了，此时生产者进入阻塞队列中等待
        }
        q.push(d);//将数据推给缓冲区
        pthread_mutex_unlock(&mutex_);
        pthread_cond_signal(&c_cond_);//此时缓冲区中至少有一个数据，消费者就可以开始消费了
    }
    void pop(T* out)//对于输出参数用 * //对于输入输出参数用 &
    {
        pthread_mutex_lock(&mutex_);
        while(is_empty())
        {
            pthread_cond_wait(&c_cond_, &mutex_);
        }
        *out = q.front();//消费者从缓冲区读取数据
        q.pop();
        pthread_mutex_unlock(&mutex_);
        pthread_cond_signal(&p_cond_);//因为消费者从缓冲区拿了一个数据，所以缓冲区中至少有一个空位
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&p_cond_);
        pthread_cond_destroy(&c_cond_);
    }
private:
    bool is_empty()
    {
        return q.empty();
    }
    bool is_max()
    {
        return q.size() == max_;
    }
private:
    int max_;//该阻塞队列的最大值
    std::queue<T> q;//阻塞队列
    pthread_mutex_t mutex_;//互斥锁
    pthread_cond_t p_cond_;//生产者条件变量
    pthread_cond_t c_cond_;//消费者条件变量
};

